package cn.yangliu.webflux;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;

/**
 * @author 杨柳
 * @date 2019-06-07
 */
public class IpUtils {

    private static final String IP_REGEX = "^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-4]|2[0-4]\\d|[01]?\\d\\d?)$";

    public static boolean check(String ipInfo) {
        String[] array = ipInfo.split(",");
        for (String s : array) {
            if (!checkIp(s)) {
                return false;
            }
        }

        return true;
    }

    private static boolean checkIp(String ip) {
        if (StringUtils.isEmpty(ip)) {
            return false;
        }

        /*
            符合正则匹配 直接返回true
         */
        if (ip.matches(IP_REGEX)) {
            return true;
        }

        /**
         * 不能包含两种通配
         */
        if (ip.contains("*") && ip.contains("-")) {
            return false;
        }
        return checkIpLength(ip);
    }

    /**
     * 检查IP长度
     *
     * @param ip
     * @return
     */
    private static boolean checkIpLength(String ip) {
        String[] array = ip.split("\\.");
        if (array.length != 4) {
            return false;
        }
        for (int i = 0; i < array.length; i++) {
            boolean flag = checkSegmentation(i, array[i]);

            if (!flag) {
                return flag;
            }
        }

        return true;
    }

    /**
     * 分段检查
     *
     * @param index        第n段
     * @param segmentation 值
     * @return 结果
     */
    private static boolean checkSegmentation(int index, String segmentation) {
        if (Objects.equals(segmentation, "*")) {
            return index == 3;
        }

        if (segmentation.contains("-")) {
            if (index != 3) {
                return false;
            }
        }

        //每个分段两位以上 但以0开头 也不合法 比如 39.01.72.132 也不合法 正确的写法应该是39.1.72.132
        if (segmentation.length() > 1 && segmentation.startsWith("0")) {
            return false;
        }
        if (segmentation.contains("-")) {
            String[] array = segmentation.split("-");
            if (array.length == 0) {
                return false;
            }
            try {
                int min = Integer.parseInt(array[0]);
                int max = Integer.parseInt(array[1]);
                if (min >= max) {
                    return false;
                }

                if (index < 3 && (min <= 0 || max > 255)) {
                    return false;
                }

                if (min <= 1 || max >= 255) {
                    return false;
                }
            } catch (Exception e) {
                return false;
            }

        }
        try {
            int segmentationNum = Integer.parseInt(segmentation);
            if (index < 3 && (segmentationNum <= 0 || segmentationNum > 255)) {
                return false;
            }
            if (index == 3 && (segmentationNum <= 1 || segmentationNum >= 255)) {
                return false;
            }
        } catch (Exception e) {
            if (index != 3) {
                return false;
            }
        }
        return true;
    }

    public static String merge(String ipInfo) {
        if (StringUtils.isEmpty(ipInfo)) {
            return "";
        }
        if (!ipInfo.contains(",")) {
            return ipInfo;
        }
        String[] strings = ipInfo.split(",");

        //包含通配的ip *
        List<String> includeWildcard1 = new ArrayList<>();
        //包含通配的ip段 -
        List<String> includeWildcard2 = new ArrayList<>();
        //不包含通配的ip
        List<String> notIncludeWildcard = new ArrayList<>();

        for (String string : strings) {
            if (string.contains("*")) {
                includeWildcard1.add(string);
                continue;
            }

            if (string.contains("-")) {
                includeWildcard2.add(string);
                continue;
            }
            notIncludeWildcard.add(string);
        }
        //去重
        includeWildcard1 =
                includeWildcard1.stream().distinct().collect(Collectors.toList());
        includeWildcard2 =
                includeWildcard2.stream().distinct().collect(Collectors.toList());
        notIncludeWildcard =
                notIncludeWildcard.stream().distinct().collect(Collectors.toList());

        List<String> notIncludeWildcardTemp = new ArrayList<>(notIncludeWildcard);
        //如果不包含通配符的集合不为空
        if (!notIncludeWildcard.isEmpty()) {
            //包含通配的集合为空 换言之 全是一个一个的正常ip
            if (includeWildcard1.isEmpty() && includeWildcard2.isEmpty()) {
                return ipInfo;
            }

            //检查不包含通配的是否在通配集合中
            for (String includeWildcardIp : includeWildcard1) {
                for (String notIncludeWildcardIp : notIncludeWildcard) {
                    //包含关系 则删除单独的不包含通配的IP
                    if (isInclude(includeWildcardIp, notIncludeWildcardIp)) {
                        notIncludeWildcardTemp.remove(notIncludeWildcardIp);
                    }
                }
            }
            notIncludeWildcard = notIncludeWildcardTemp;

            //检查不包含通配的是否在通配集合中
            for (String includeWildcardIp : includeWildcard2) {
                for (String notIncludeWildcardIp : notIncludeWildcard) {
                    //包含关系 则删除单独的不包含通配的IP
                    if (isInclude(includeWildcardIp, notIncludeWildcardIp)) {
                        notIncludeWildcardTemp.remove(notIncludeWildcardIp);
                    }
                }
            }
            notIncludeWildcard = notIncludeWildcardTemp;

        }


        //用范围表示后的ip段集合
        List<String> result1 = new ArrayList<>();
        //该集合中的元素可能被删除 被保留下来的是独立ip 不能用范围表示的ip
        List<String> bakList = new ArrayList<>(notIncludeWildcard);
        merge1(notIncludeWildcard, bakList, result1);

        //检查包含通配的集合中是否相互有重复 合并-和 -
        //该集合中的元素可能被删除 被保留下来的是合并后的结果集
        List<String> result2 = new ArrayList<>(includeWildcard2);
        merge2(includeWildcard2, includeWildcard2, result2);
        result2 = result2.stream().distinct().collect(Collectors.toList());

        //检查包含通配的集合中是否相互有重复 合并*和 -
        //该集合中的元素可能被删除 被保留下来的是合并后的结果集
        List<String> result3 = new ArrayList<>(result2);
        merge3(includeWildcard1, result3, result2);


        result1.addAll(result2);
        result1.addAll(includeWildcard1);
        result1.addAll(bakList);
        //去重
        result1 = result1.stream().distinct().sorted().collect(Collectors.toList());
        return result1.toString().replace("[", "").replace("]", "").replace(" ", "");

    }


    /**
     * merge -和-
     *
     * @param list1
     * @param list2
     * @param tempList
     */
    private static void merge2(List<String> list1, List<String> list2, List<String> tempList) {
        for (String ip1 : list1) {
            String[] ipSegmentations = ip1.split("\\.");
            String temp1 = ip1.substring(0, ip1.lastIndexOf("."));
            for (String ip2 : list2) {
                if (Objects.equals(ip1, ip2)) {
                    continue;
                }

                String[] ip2Segmentations = ip2.split("\\.");

                String temp2 = ip2.substring(0, ip2.lastIndexOf("."));


                if (!temp1.equals(temp2)) {
                    continue;
                }

                if (ipSegmentations[3].contains("-") && ip2Segmentations[3].contains("-")) {
                    String[] array1 = ipSegmentations[3].split("-");
                    String[] array2 = ip2Segmentations[3].split("-");
                    int min1 = Integer.parseInt(array1[0]);
                    int max1 = Integer.parseInt(array1[1]);
                    int min2 = Integer.parseInt(array2[0]);
                    int max2 = Integer.parseInt(array2[1]);

                    if (max1 >= min2 && max2 > min1) {
                        int remainMin = min1 >= min2 ? min2 : min1;
                        int remainMax = max1 >= max2 ? max1 : max2;

                        String substring = temp1 + "." + remainMin + "-" + remainMax;

                        tempList.remove(ip1);
                        tempList.remove(ip2);
                        tempList.add(substring);
                    }
                }
            }
        }
    }

    /**
     * 检查是否是包含关系
     *
     * @param includeWildcardIp    带通配的Ip * 或者 -
     * @param notIncludeWildcardIp 不带通配的Ip
     * @return 是否是包含关系
     */
    private static boolean isInclude(String includeWildcardIp, String notIncludeWildcardIp) {
        String[] strings = includeWildcardIp.split("\\.");
        String[] segmentations = notIncludeWildcardIp.split("\\.");
        boolean flag = false;
        for (int i = 0; i < strings.length; i++) {
            String s = strings[i];
            if (s.contains("-")) {
                String[] split = s.split("-");
                int min = Integer.parseInt(split[0]);
                int max = Integer.parseInt(split[1]);
                int target = Integer.parseInt(segmentations[i]);

                //在范围以内
                if (min <= target && max >= target) {
                    flag = true;
                    continue;
                }

                //不在范围以内
                break;
            }
            if (s.equals("*")) {
                flag = true;
                continue;
            }
            //当前段不包含通配 且两个段不相同 直接返回false
            if (!Objects.equals(s, segmentations[i])) {
                return false;
            }
        }
        return flag;
    }

    /**
     * merge * 和 -
     *
     * @param list1  包含*通配的集合
     * @param list2  包含 - 的通配集合
     * @param reulst 合并结果
     */
    private static void merge3(List<String> list1, List<String> list2, List<String> reulst) {
        for (String ip1 : list1) {
            String temp1 = ip1.substring(0, ip1.lastIndexOf("."));
            for (String ip2 : list2) {
                String temp2 = ip2.substring(0, ip2.lastIndexOf("."));
                if (temp1.equals(temp2)) {
                    reulst.remove(ip2);
                }
            }
        }
    }

    /**
     * @param source  原数据
     * @param bakList 备份数据
     * @param result  合并结果
     */
    //相邻ip合并为范围表示
    private static void merge1(List<String> source, List<String> bakList, List<String> result) {

        for (String s : source) {
            String[] arrary = s.split("\\.");
            String prefix = s.substring(0, s.lastIndexOf("."));
            int num = Integer.parseInt(arrary[3]);
            String nextIp = prefix + "." + (num + 1);

            if (bakList.contains(nextIp)) {
                int end = num + 1;
                String mergeIp = prefix + "." + num + "-" + end;
                result.add(mergeIp);
                bakList.remove(s);
                bakList.remove(nextIp);
                continue;
            }

            if (result.isEmpty()) {
                continue;
            }
            for (String ip : result) {
                if (isInclude(ip, s)) {
                    bakList.remove(s);
                }
            }

        }
        List<String> merges = new ArrayList<>(result);
        merge2(result, result, merges);
        result.clear();
        result.addAll(merges);
    }


}
